#include "stdafx.h"
#include "CppUnitTest.h"
#include <HSModel/Validator.h>
#include <HSModel/Ground.h>
#include <HSModel/GameController.h>

#include <HSModel/SharedConstants.h>
#include <HSModel/Model3DStorage.h>
#include <HSModel/World.h>
#include <HSModel/ObjParser.h>
#include <HSModel/WorldInfoController.h>
#include <HSModel/FindWaterAreas.h>
#include <HSModel/Water.h>

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace HSModelTests
  {
  TEST_CLASS(ValidatorTest)
    {
    public:

      TEST_METHOD(TreeCantBeOnWalkway)
        {
        World world;
        world.GetGround()->CreateWalkway(TVector2(5, 5));
				Validator validator;
				Assert::IsFalse(validator.CanObjectBePlacedHere(TVector3(5, 5, 5), world, GameController(), WE_TREE));
        }
			TEST_METHOD(TreeCanBeOnWalkway)
        {
        World world;
				world.GetGround()->CreateWalkway(TVector2(5, 5));
				Validator validator;
				Assert::IsTrue(validator.CanObjectBePlacedHere(TVector3(25, 25, 25), world, GameController(), WE_TREE));
        }

      TEST_METHOD(BenchCanNotBePlacedOnWalkway)
        {
        /* ---- Arrange ---- */
        HeightMap height_map = HeightMap(GroundConstants::HEIGHTMAP_SIZE, GroundConstants::HEIGHTMAP_SIZE);
        GeneratedHeightMapWithDiamondSquare(height_map, GroundConstants::PLAINS_PERCENTAGE, GroundConstants::MAX_HEIGHT);

        World world;
        Validator validator;
        GameController game_controller;

         /*
         * .............
         * : +   W   + :
         * :           :
         * : W   W   W :
         * :           :
         * : +   W   + :
         * .............
         * Walkway squares marked with "W"
         * Bench can't be placed on "W" squares
         */
        
        world.GetGround()->CreateWalkway(TVector2(3, 2) * GroundConstants::CELL_SIZE);
        world.GetGround()->CreateWalkway(TVector2(2, 2) * GroundConstants::CELL_SIZE);
        world.GetGround()->CreateWalkway(TVector2(4, 2) * GroundConstants::CELL_SIZE);
        world.GetGround()->CreateWalkway(TVector2(3, 1) * GroundConstants::CELL_SIZE);
        world.GetGround()->CreateWalkway(TVector2(3, 3) * GroundConstants::CELL_SIZE);

        /* ---- Act ----*/
        /* ---- Assert ---- */
        Assert::IsFalse(validator.CanObjectBePlacedHere(TVector3(3, 2, 0) * GroundConstants::CELL_SIZE, world, game_controller, WE_BENCH));
        Assert::IsFalse(validator.CanObjectBePlacedHere(TVector3(2, 2, 0) * GroundConstants::CELL_SIZE, world, game_controller, WE_BENCH));
        Assert::IsFalse(validator.CanObjectBePlacedHere(TVector3(4, 2, 0) * GroundConstants::CELL_SIZE, world, game_controller, WE_BENCH));
        Assert::IsFalse(validator.CanObjectBePlacedHere(TVector3(3, 1, 0) * GroundConstants::CELL_SIZE, world, game_controller, WE_BENCH));
        Assert::IsFalse(validator.CanObjectBePlacedHere(TVector3(3, 3, 0) * GroundConstants::CELL_SIZE, world, game_controller, WE_BENCH));
        }

      TEST_METHOD(BenchCanBePlacedOnlyNearWalkway)
        {
        /* ---- Arrange ---- */
        HeightMap height_map = HeightMap(GroundConstants::HEIGHTMAP_SIZE, GroundConstants::HEIGHTMAP_SIZE);
        GeneratedHeightMapWithDiamondSquare(height_map, GroundConstants::PLAINS_PERCENTAGE, GroundConstants::MAX_HEIGHT);

        World world;
        Validator validator;
        GameController game_controller;

        /*
         * .............
         * : -   +   - :
         * :           :
         * : +   W   + :
         * :           :
         * : -   +   - :
         * .............
         * Walkway is in center square
         * Bench can be placed if it touches the side of walkway
         * Bench can be placed on "+" squares and can not on "-" squares
         */
        world.GetGround()->CreateWalkway(TVector2(3, 2) * GroundConstants::CELL_SIZE);

        /* ---- Act ---- */
        /* ---- Assert ---- */

        /* Bench can be placed if it touch touches side of walkway*/
        Assert::IsTrue(validator.CanObjectBePlacedHere(TVector3(2, 2, 0) * GroundConstants::CELL_SIZE, world, game_controller, WE_BENCH));
        Assert::IsTrue(validator.CanObjectBePlacedHere(TVector3(4, 2, 0) * GroundConstants::CELL_SIZE, world, game_controller, WE_BENCH));
        Assert::IsTrue(validator.CanObjectBePlacedHere(TVector3(3, 1, 0) * GroundConstants::CELL_SIZE, world, game_controller, WE_BENCH));
        Assert::IsTrue(validator.CanObjectBePlacedHere(TVector3(3, 3, 0) * GroundConstants::CELL_SIZE, world, game_controller, WE_BENCH));

        /* Bench can't be placed if it touches the corner of walkway*/
        Assert::IsFalse(validator.CanObjectBePlacedHere(TVector3(2, 1, 0) * GroundConstants::CELL_SIZE, world, game_controller, WE_BENCH));
        Assert::IsFalse(validator.CanObjectBePlacedHere(TVector3(4, 1, 0) * GroundConstants::CELL_SIZE, world, game_controller, WE_BENCH));
        Assert::IsFalse(validator.CanObjectBePlacedHere(TVector3(2, 3, 0) * GroundConstants::CELL_SIZE, world, game_controller, WE_BENCH));
        Assert::IsFalse(validator.CanObjectBePlacedHere(TVector3(4, 3, 0) * GroundConstants::CELL_SIZE, world, game_controller, WE_BENCH));

        /* Bench can't be placed in the middle of the open*/
        Assert::IsFalse(validator.CanObjectBePlacedHere(TVector3(5, 7, 0) * GroundConstants::CELL_SIZE, world, game_controller, WE_BENCH));
        }

      TEST_METHOD(BenchCanBePlacedOnlyInFreeCell)
        {
        /* ---- Arrange ---- */
        HeightMap height_map = HeightMap(GroundConstants::HEIGHTMAP_SIZE, GroundConstants::HEIGHTMAP_SIZE);
        GeneratedHeightMapWithDiamondSquare(height_map, GroundConstants::PLAINS_PERCENTAGE, GroundConstants::MAX_HEIGHT);

        World world;

        Validator validator;
        GameController game_controller;

        /*
         * .............
         * : -   T   - :
         * :           :
         * : T   W   + :
         * :           :
         * : -   +   - :
         * .............
         * (W)alkway, (T)ree
         * Bench can't be placed on squares with other objects
         * Bench can be placed on "+" squares
         */
        world.GetGround()->CreateWalkway(TVector2(3, 2) * GroundConstants::CELL_SIZE);
        world.AddTree(TVector3(2.3, 2.6, 0) * GroundConstants::CELL_SIZE);
        world.AddTree(TVector3(3.2, 1.7, 1) * GroundConstants::CELL_SIZE);
        world.GenerateChangeEvent();

        /* ---- Act ---- */
        /* ---- Assert ---- */
        Assert::IsFalse(validator.CanObjectBePlacedHere(TVector3(2, 2, 0) * GroundConstants::CELL_SIZE, world, game_controller, WE_BENCH));
        Assert::IsFalse(validator.CanObjectBePlacedHere(TVector3(3, 1, 0) * GroundConstants::CELL_SIZE, world, game_controller, WE_BENCH));

        Assert::IsTrue(validator.CanObjectBePlacedHere(TVector3(3, 3, 0) * GroundConstants::CELL_SIZE, world, game_controller, WE_BENCH));
        Assert::IsTrue(validator.CanObjectBePlacedHere(TVector3(4, 2, 0) * GroundConstants::CELL_SIZE, world, game_controller, WE_BENCH));
        }

      TEST_METHOD(NoTerrainChangeOnWater)
        {
        HeightMap height_map = HeightMap(GroundConstants::CELL_COUNT + 1, GroundConstants::CELL_COUNT + 1);
        GeneratedHeightMapWithDiamondSquare(height_map, GroundConstants::PLAINS_PERCENTAGE, GroundConstants::MAX_HEIGHT);

        Ground ground(height_map, GroundConstants::CELL_SIZE, GroundConstants::WATER_LEVEL);

        THMapArea area;
        area.level = 0;
        area.points.insert(THMapPoint(0, 0));

        std::shared_ptr<Model3D> water_model(new Model3D());
        water_model->AddPoint(0, 0, 0);


        ground.AddWaterModel(std::shared_ptr<Water>(new Water(TVector3(0,0,0), area,
          water_model)));

        std::map<std::string, IRenderable*> v;

        WorldInfoController control(ground, v);

        Model3D changed_tr;
        auto p1 = changed_tr.AddPoint(0, 0, 0);
        auto p2 = changed_tr.AddPoint(0, 1, 0);
        auto p3 = changed_tr.AddPoint(1, 0, 0);

        changed_tr.AddTriangle(p1, p2, p3);

        Validator val;
        Assert::IsFalse(val.CanChangeTerrain(changed_tr, control));
        }

      TEST_METHOD(ShouldNotCreateWaterOnWalkway)
        {
        HeightMap height_map = HeightMap(3,3);
        height_map.Set(0, 0, 0);
        height_map.Set(0, 1, 0);
        height_map.Set(0, 2, 0);
        height_map.Set(1, 0, 0);
        height_map.Set(1, 1, -10);
        height_map.Set(1, 2, 0);
        height_map.Set(2, 0, 0);
        height_map.Set(2, 1, 0);
        height_map.Set(2, 2, 0);


        Ground ground(height_map, GroundConstants::CELL_SIZE, GroundConstants::WATER_LEVEL);

        ground.CreateWalkway(TVector2(15,15));
        auto possible_water = HeightMapUtils::GetWaterModels(height_map, -0.1);
        
        std::map<std::string, IRenderable*> v;
        WorldInfoController control(ground, v);
        
        Validator water_validator;
        bool real_validation =  water_validator.CanPlaceWater(possible_water[0]->GetModel(), control);
        Assert::IsFalse(real_validation);
        }

      TEST_METHOD(ShouldCreateNotAllWaterOnWalkway)
        {
        HeightMap height_map = HeightMap(3,3);
        height_map.Set(0, 0, 0);
        height_map.Set(0, 1, 0);
        height_map.Set(0, 2, -20);
        height_map.Set(1, 0, -10);
        height_map.Set(1, 1, 0);
        height_map.Set(1, 2, 0);
        height_map.Set(2, 0, 0);
        height_map.Set(2, 1, 0);
        height_map.Set(2, 2, 0);


        Ground ground(height_map, GroundConstants::CELL_SIZE, GroundConstants::WATER_LEVEL);

        ground.CreateWalkway(TVector2(0,0));
        auto possible_water = HeightMapUtils::GetWaterModels(height_map, -0.1);
        
        std::map<std::string, IRenderable*> v;
        WorldInfoController control(ground, v);
        
        Validator water_validator;
        bool real_validation =  water_validator.CanPlaceWater(possible_water[0]->GetModel(), control);
        Assert::IsTrue(real_validation);
        
        real_validation =  water_validator.CanPlaceWater(possible_water[1]->GetModel(), control);
        Assert::IsFalse(real_validation);
        }

      
      TEST_METHOD(BalloonsShopCantBeOnWalkway)
        {
        World world(HeightMap(5, 5));
        world.GetGround()->CreateWalkway(TVector2(0, 0)); // 0, 0
        world.GetGround()->CreateWalkway(TVector2(10, 0)); // 1, 0

        Validator validator;
        Assert::IsFalse(validator.CanObjectBePlacedHere(TVector3(0, 0, 0), world, GameController(),
          WE_BALLOONS_SHOP));
        }

      TEST_METHOD(BalloonsShopCanBePlacedNearWalkway)
        {
        World world(HeightMap(5, 5));
        world.GetGround()->CreateWalkway(TVector2(10, 0)); // 1, 0

        Validator validator;
        Assert::IsTrue(validator.CanObjectBePlacedHere(TVector3(0, 0, 0), world, GameController(),
          WE_BALLOONS_SHOP));
        }

      TEST_METHOD(CantPlaceFerrisWheelNearMapEdge)
        {
        using namespace GroundConstants;

        World world;

        Validator validator;

        TVector3 pos_1 = TVector3(CELL_SIZE / 2, 10 * CELL_SIZE / 2, 0);
        TVector3 pos_2 = TVector3(10 * CELL_SIZE / 2, 3* CELL_SIZE / 2, 0);

        TVector3 pos_3 = TVector3(CELL_COUNT*CELL_SIZE - CELL_SIZE / 2, CELL_SIZE / 2, 0);
        TVector3 pos_4 = TVector3(3 * CELL_SIZE / 2, CELL_COUNT * CELL_SIZE - CELL_SIZE / 2, 0);

        Assert::IsFalse(validator.CanObjectBePlacedHere(pos_1, world, GameController(), WE_FERRIS));
        Assert::IsFalse(validator.CanObjectBePlacedHere(pos_2, world, GameController(), WE_FERRIS));
        Assert::IsFalse(validator.CanObjectBePlacedHere(pos_3, world, GameController(), WE_FERRIS));
        Assert::IsFalse(validator.CanObjectBePlacedHere(pos_4, world, GameController(), WE_FERRIS));
        }

      TEST_METHOD(CantPlaceFerrisWheelIfRectangleIsNotFree)
        {
        using namespace GroundConstants;
        World world;
        Validator validator;

        TVector2 object_pos = TVector2( 5.5 * CELL_SIZE, 5.5 * CELL_SIZE);
        TVector3 ferris_pos = TVector3( 6.5 * CELL_SIZE, 6.5 * CELL_SIZE, 0);

        world.AddBench(object_pos);

        world.GenerateChangeEvent();
        Assert::IsFalse(validator.CanObjectBePlacedHere(ferris_pos, world, GameController(), WE_FERRIS));
        }

      TEST_METHOD(GrassCanBePlacedOnWalkwayNearTree)
        {
        using namespace GroundConstants;

        World world(HeightMap(5, 5));
        Validator validator;
        world.GetGround()->CreateWalkway(TVector2(0 * CELL_SIZE, 0 * CELL_SIZE)); // 0, 0
        world.AddTree(TVector3(0 * CELL_SIZE, 1 * CELL_SIZE, 0)); // 0, 1

        Assert::IsTrue(validator.CanObjectBePlacedHere(TVector3(0 * CELL_SIZE, 0 * CELL_SIZE, 0),
          world, GameController(), WE_GRASS));
        }

      TEST_METHOD(GrassCanNotBePlacedNearBallonsShopOrBench)
        {
        using namespace GroundConstants;

        World world(HeightMap(5, 5));
				Validator validator;
        world.GetGround()->CreateWalkway(TVector2(0 * CELL_SIZE, 0 * CELL_SIZE)); // 0, 0
        world.AddBalloonsShop(TVector3(0 * CELL_SIZE, 1 * CELL_SIZE, 0)); // 0, 1
        world.GenerateChangeObjectWithoutLandscapeEvent(); // updating WorldInfoController

        Assert::IsFalse(validator.CanObjectBePlacedHere(TVector3(0 * CELL_SIZE, 0 * CELL_SIZE, 0),
          world, GameController(), WE_GRASS)); // 0, 0

        World world2(HeightMap(5, 5));
        world2.GetGround()->CreateWalkway(TVector2(0 * CELL_SIZE, 0 * CELL_SIZE)); // 0, 0
        world2.AddBench(TVector2(0 * CELL_SIZE, 1 * CELL_SIZE)); // 0, 1
        world2.GenerateChangeObjectWithoutLandscapeEvent(); // updating WorldInfoController

        Assert::IsFalse(validator.CanObjectBePlacedHere(TVector3(0 * CELL_SIZE, 0 * CELL_SIZE, 0),
          world, GameController(), WE_GRASS)); // 0, 0
        }

      TEST_METHOD(FerrisWheelCannotBePlacedOnWalkway)
        {
        using namespace GroundConstants;

        Validator validator;
        World world(HeightMap(15, 15));
        
        world.GetGround()->CreateWalkway(TVector2(1.5 * CELL_SIZE, 1.5 * CELL_SIZE));
        world.GetWorldInfoController()->SetGroundType(TVector2(1.5 * CELL_SIZE, 1.5 * CELL_SIZE), GT_WALKWAY);
        bool can_place = validator.CanObjectBePlacedHere(TVector3(2.5 * CELL_SIZE, 2.5 * CELL_SIZE, 0), world, GameController(), EWorldEntityTypes::WE_FERRIS);
        Assert::IsFalse(can_place);       
        }

      TEST_METHOD(BalloonShopCannotBePlacedOnCellWithBigHeightDifference)
        {
        HeightMap height_map(5, 5);
        height_map.Set(0, 1, 100);
        World world(height_map);
        world.GetGround()->CreateWalkway(TVector2(0, 20));

        Validator validator;
        Assert::IsFalse(validator.CanObjectBePlacedHere(TVector3(0, 10, 0), world, GameController(),
          WE_BALLOONS_SHOP));    
        }
    };
  }